package cc.catface.serverorder;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.EventObserverRegistry;
import com.alibaba.csp.sentinel.util.TimeUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@RestController
public class OrderController {

    /**
     * order服务测试接口
     */
    @GetMapping("order")
    public String order() {
        return "order";
    }

    @Resource
    private RestTemplate restTemplate;

    /**
     * RPC调用account服务的接口
     */
    @Value("${service-url.nacos-user-service}")
    private String accountURL;

    @GetMapping("getAccount")
    public String getAccount() {
        return restTemplate.getForObject(accountURL + "/account", String.class);
    }

    /**
     * 获取nacos服务的配置信息(实时刷新)
     */
    @Value("${service-url.nacos-config-service}")
    private String configURL;

    @GetMapping("getConfig")
    public String getConfig() {
        return restTemplate.getForObject(configURL + "/getConfigInfo", String.class);
    }


    /**
     * ==========
     * sentinel *
     * ==========
     */
    /*降级(降级方法的形参必须和接口一致且可多一个Throwable) | 当符合降级规则则不执行接口方法直接走降级方法*/
    //简单降级(发生异常走fallback)-降级方法在当前类
    @GetMapping("fallThis")
    @SentinelResource(value = "fallThisClass", fallback = "fallThis")
    public String fallThisClass() {
        System.out.println("fallThisClass(), at:" + System.currentTimeMillis());
        throw new RuntimeException("主动抛出异常(this)");
    }

    public String fallThis(Throwable t) {
        System.out.println("fallThis(), at:" + System.currentTimeMillis());
        return "主动抛出异常过多, 服务降级(fallThis()), throwable:" + t.getMessage();
    }

    //简单降级(发生异常走fallback)-降级方法在其他类(必须是public static)
    @GetMapping("fallOther")
    @SentinelResource(value = "fallOtherClass", fallbackClass = {FallbackService.class}, fallback = "fallOther")
    public String fallOtherClass() {
        System.out.println("fallOtherClass(), at:" + System.currentTimeMillis());
        throw new RuntimeException("主动抛出异常(other)");
    }

    @Resource
    FeignService feignService;

    //熔断降级-调用RPC方法降级
    @GetMapping("AAccount/{id}")
    @SentinelResource(value = "AAccount", fallback = "fallAAccount")
    public String AAccount(@PathVariable("id") int id) {
        monitor();
        System.out.println("AAccount(), at:" + System.currentTimeMillis());
        if (id == -1) throw new RuntimeException("id不能等于-1!");
        return feignService.AAccount();
    }

    public String fallAAccount(@PathVariable("id") int id, Throwable t) {
        System.out.println("fallAAccount(), at:" + System.currentTimeMillis());
        return "调用account服务的account方法降级:" + t.getMessage();
    }

    //熔断降级-代码定义熔断降级规则
    @GetMapping("fallDiy")
    @SentinelResource(value = "fallDiy", fallback = "fallbackRule")
    public String fallDiy() {
        initDegradeRule();
        monitor();
        System.out.println("fallDiy(), at:" + System.currentTimeMillis());
        throw new RuntimeException("fallbackRule直接抛异常[看自定义熔断规则生效]");
    }

    public String fallbackRule() {
        System.out.println("fallbackRule(), at:" + System.currentTimeMillis());
        return "自定义熔断规则的降级方法fallbackRule()执行";
    }

    private static void initDegradeRule() {
        List<DegradeRule> rules = new ArrayList<>();
        DegradeRule rule = new DegradeRule("fallDiy");
        rule.setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType());
        rule.setCount(0.7);
        rule.setMinRequestAmount(3);
        rule.setStatIntervalMs(3000);
        rule.setTimeWindow(10);
        rules.add(rule);
        DegradeRuleManager.loadRules(rules);
    }

    /**
     * 自定义事件监听器，监听熔断器状态转换
     */
    public void monitor() {
        EventObserverRegistry.getInstance().addStateChangeObserver("logging", (prevState, newState, rule, snapshotValue) -> {
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            if (newState == CircuitBreaker.State.OPEN) {
                //变换至OPEN状态时会携带触发时的值
                System.err.println(String.format("%s -> OPEN at %s, 发送请求次数=%.2f", prevState.name(),
                        format.format(new Date(TimeUtil.currentTimeMillis())), snapshotValue));
            } else {
                System.err.println(String.format("%s -> %s at %s", prevState.name(), newState.name(),
                        format.format(new Date(TimeUtil.currentTimeMillis()))));
            }
        });
    }
}
